// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ZIRCON_SYSTEM_UAPP_IHDA_CODEC_STATE_H_
#define ZIRCON_SYSTEM_UAPP_IHDA_CODEC_STATE_H_

#include <stddef.h>
#include <stdint.h>
#include <zircon/assert.h>
#include <zircon/types.h>

#include <fbl/unique_ptr.h>
#include <intel-hda/utils/codec-caps.h>
#include <intel-hda/utils/codec-commands.h>
#include <intel-hda/utils/codec-state.h>

namespace audio {
namespace intel_hda {

struct AudioWidgetState;
using AudioWidgetStatePtr = fbl::unique_ptr<AudioWidgetState>;

struct FunctionGroupState;
struct AudioFunctionGroupState;
using FunctionGroupStatePtr = fbl::unique_ptr<FunctionGroupState>;

struct PowerState {
  // Section 7.3.4.12 : Supported Power States
  uint32_t supported_states_;

  // Section 7.3.3.10 : Current power state
  uint8_t set_;
  uint8_t active_;
  bool error_;
  bool clock_stop_ok_;
  bool settings_reset_;
};

// Section 7.3.3.14.  Present only in nodes (function groups and widgets) whose
// capabilities indicate the ability to send unsolicited responses.
struct UnsolicitedResponseState {
  bool enabled() const { return (raw_data_ & 0x80) != 0; }
  uint8_t tag() const { return static_cast<uint8_t>(raw_data_ & 0x3f); }
  uint8_t raw_data_;
};

struct AudioWidgetState {
  struct StreamFormat {
    // Stream format bitfields documented in section 3.7.1
    static constexpr uint16_t FLAG_NON_PCM = (1u << 15);

    uint32_t BASE() const { return (raw_data_ & (1u << 14)) ? 44100 : 48000; }
    uint32_t CHAN() const { return (raw_data_ & 0xF) + 1; }
    uint32_t DIV() const { return ((raw_data_ >> 8) & 0x7) + 1; }
    uint32_t MULT() const {
      uint32_t bits = (raw_data_ >> 11) & 0x7;
      if (bits >= 4)
        return 0;
      return bits + 1;
    }
    uint32_t BITS() const {
      switch ((raw_data_ >> 4) & 0x7) {
        case 0:
          return 8u;
        case 1:
          return 16u;
        case 2:
          return 20u;
        case 3:
          return 24u;
        case 4:
          return 32u;
        default:
          return 0u;
      }
    }

    bool is_pcm() const { return (raw_data_ & FLAG_NON_PCM) == 0; }
    uint32_t sample_rate() const { return (BASE() * MULT()) / DIV(); }
    uint32_t channels() const { return CHAN(); }
    uint32_t bits_per_chan() const { return BITS(); }

    uint16_t raw_data_;
  };

  struct AmpState {
    uint8_t gain[2];
    bool mute[2];
  };

  struct ConnListEntry {
    bool range_;
    uint16_t nid_;
    AmpState amp_state_;
  };

  explicit AudioWidgetState(const AudioWidgetCaps& caps) : caps_(caps) {}

  const AudioWidgetCaps caps_;
  const AudioFunctionGroupState* afg_ = nullptr;
  uint16_t nid_;

  // Note: to simplify life, the widget struct contains the union of all of
  // the different field which may be needed for any type of audio widget.
  // Not all of the fields will be meaningful depending on the widget type.
  uint32_t pcm_size_rate_;  // Section 7.3.4.7 : Supported PCM sizes and rates
  uint32_t pcm_formats_;    // Section 7.3.4.8 : Supported PCM formats
  uint32_t pin_caps_ = 0;   // Section 7.3.4.9 : Pin Capabilities
  StreamFormat cur_format_;

  // Section 7.3.3.11 : Stream tag and channel routing for converters.
  uint8_t stream_tag_;
  uint8_t stream_chan_;

  // Section 7.3.4.10 : Amplifier capabilities
  AmpCaps input_amp_caps_;
  AmpCaps output_amp_caps_;

  // Section 7.3.3.7 : Amplifier Gain/Mute state
  AmpState input_amp_state_;
  AmpState output_amp_state_;

  // Sections 7.3.3.2, 7.3.3.3 & 7.3.4.11 : Connection List
  bool long_form_conn_list_;
  uint8_t conn_list_len_;
  fbl::unique_ptr<ConnListEntry[]> conn_list_;
  uint16_t connected_nid_;
  uint8_t connected_nid_ndx_;

  // Sections 7.3.4.12 & 7.3.3.10.
  PowerState power_;

  // Section 7.3.4.13 : Processing Capabilities
  bool can_bypass_processing_;
  uint8_t processing_coefficient_count_;

  // Section 7.3.4.15 : Volume Knob Capabilities
  bool vol_knob_is_delta_;
  uint8_t vol_knob_steps_;

  // Section 7.3.3.31.  Present only in pin complexes
  ConfigDefaults cfg_defaults_;

  // Section 7.3.3.12.  Present only in pin complexes
  PinWidgetCtrlState pin_widget_ctrl_;

  // Section 7.3.3.14.
  UnsolicitedResponseState unsol_resp_ctrl_;

  // Section 7.3.3.15
  //
  // Only valid for pin complexes, only run if the pin complex supports
  // presence detect and the config defaults do not indicate a jack detect
  // override.
  PinSenseState pin_sense_;
  bool pin_sense_valid_ = false;

  // Section 7.3.3.16 : External amp power down state
  EAPDState eapd_state_;
};

struct FunctionGroupState {
  virtual ~FunctionGroupState() {}

  enum class Type : uint8_t {
    AUDIO = 0x01,
    MODEM = 0x02,
    VENDOR_START = 0x80,
    VENDOR_END = 0xFF,
  };

  // Section 7.3.3.30
  struct ImplementationID {
    uint32_t BoardImplID() const { return (raw_data_ >> 8) & 0xFFFFFF; }
    uint16_t BoardMfrID() const { return static_cast<uint16_t>(raw_data_ >> 16); }
    uint8_t BoardSKU() const { return static_cast<uint8_t>((raw_data_ >> 8) & 0xFF); }
    uint8_t AssemblyID() const { return static_cast<uint8_t>(raw_data_ & 0xFF); }
    uint32_t raw_data_;
  };

  const Type type_;
  bool can_send_unsolicited_;
  uint16_t nid_;
  ImplementationID impl_id_;
  UnsolicitedResponseState unsol_resp_ctrl_;

 protected:
  explicit FunctionGroupState(Type type) : type_(type) {}
};

struct AudioFunctionGroupState : public FunctionGroupState {
  AudioFunctionGroupState() : FunctionGroupState(Type::AUDIO) {}

  AudioFunctionGroupCaps caps_;
  uint32_t default_pcm_size_rate_;  // Section 7.3.4.7 : Supported PCM sizes and rates
  uint32_t default_pcm_formats_;    // Section 7.3.4.8 : Supported PCM formats

  // Section 7.3.4.10 : Amplifier capabilities
  AmpCaps default_input_amp_caps_;
  AmpCaps default_output_amp_caps_;

  // Sections 7.3.4.12 & 7.3.3.10.
  PowerState power_;

  // Section 7.3.4.14 : GPIO Counts
  bool gpio_can_wake_;
  bool gpio_can_send_unsolicited_;
  uint8_t gpio_count_;
  uint8_t gpo_count_;
  uint8_t gpi_count_;

  uint8_t widget_count_ = 0;
  uint8_t widget_starting_id_ = 0;
  fbl::unique_ptr<AudioWidgetStatePtr[]> widgets_;
};

struct ModemFunctionGroupState : public FunctionGroupState {
  ModemFunctionGroupState() : FunctionGroupState(Type::MODEM) {}
};

struct VendorFunctionGroupState : public FunctionGroupState {
  explicit VendorFunctionGroupState(Type type) : FunctionGroupState(type) {
    ZX_DEBUG_ASSERT((type >= Type::VENDOR_START) && (type <= Type::VENDOR_START));
  }
};

struct CodecState {
  void reset() { fn_groups_.reset(); }

  uint16_t vendor_id_;
  uint16_t device_id_;

  uint8_t major_rev_;
  uint8_t minor_rev_;
  uint8_t vendor_rev_id_;
  uint8_t vendor_stepping_id_;

  uint8_t fn_group_count_;
  uint8_t fn_group_starting_id_;
  fbl::unique_ptr<FunctionGroupStatePtr[]> fn_groups_;
};

}  // namespace intel_hda
}  // namespace audio

#endif  // ZIRCON_SYSTEM_UAPP_IHDA_CODEC_STATE_H_
